/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.wsgi.activity.registry;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.wsgi.activity.WsgiActivity;
import cc.aileron.wsgi.activity.WsgiController;
import cc.aileron.wsgi.activity.WsgiProcess;
import cc.aileron.wsgi.activity.context.WsgiContext;
import cc.aileron.wsgi.activity.context.WsgiContextProvider;
import cc.aileron.wsgi.activity.message.ActivityMessage;

import com.google.inject.Provider;

/**
 * @author Aileron
 * @param <Resource>
 */
public abstract class WsgiControllerImple<Resource> implements WsgiController
{
    /**
     * @author Aileron
     */
    class WsgiActivityImpl implements WsgiActivity<Resource>
    {

        @Override
        public WsgiContext context()
        {
            return WsgiContextProvider.context();
        }

        @Override
        public ActivityMessage message()
        {
            return message;
        }

        @Override
        public Map<String, Object> paramters()
        {
            return parameters;
        }

        @Override
        public Resource resource()
        {
            return resource;
        }

        @Override
        public PojoAccessor<Resource> resourceAccessor()
        {
            return resourceAccessor;
        }

        /**
         * @param parameters
         * @param resourceAccessor
         */
        public WsgiActivityImpl(
            final Map<String, Object> parameters,
            final PojoAccessor<Resource> resourceAccessor)
        {
            this.parameters = parameters;
            this.resourceAccessor = resourceAccessor;
            this.resource = resourceAccessor.toTarget();
            final HashMap<String, String> map = new HashMap<String, String>();
            this.message = new ActivityMessage()
            {
                @Override
                public String get(final String key)
                {
                    return map.get(key);
                }

                @Override
                public Map<String, String> messageAll()
                {
                    return map;
                }

                @Override
                public void put(final String key,
                        final String value)
                {
                    map.put(key, value);
                }
            };
        }

        private final Map<String, Object> parameters;

        private final PojoAccessor<Resource> resourceAccessor;

        private final ActivityMessage message;

        private final Resource resource;
    }

    @Override
    public final void execute(final HashMap<String, Object> parameters)
    {
        final PojoAccessor<Resource> accessor = accessorProvider.get();
        final WsgiActivityImpl activity = new WsgiActivityImpl(parameters,
                accessor);
        try
        {
            try
            {
                setParameters(accessor, parameters);
                doProcess(activity);
            }
            catch (final Exception e)
            {
                final WsgiProcess<Resource> errorProcess = exceptionMappings().get(
                        e.getClass());
                if (errorProcess != null)
                {
                    try
                    {
                        errorProcess.doProcess(activity);
                        return;
                    }
                    catch (final Exception e1)
                    {
                        throw e1;
                    }
                }
                throw e;
            }
        }
        catch (final Exception e)
        {
            final StringWriter sw = new StringWriter();
            final PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            pw.close();
            logger.error(sw.toString());
        }
    }

    /**
     * @return 例外発生時の遷移先
     */
    protected abstract HashMap<Class<Exception>, WsgiProcess<Resource>> exceptionMappings();

    /**
     * @return 処理一覧
     */
    protected abstract List<WsgiValidatorAndProcess<Resource>> processList();

    /**
     * @return requestParameterKey
     */
    protected abstract List<String> requestParameterKeys();

    /**
     * @param activity
     * @throws Exception
     */
    private void doProcess(final WsgiActivityImpl activity) throws Exception
    {
        for (final WsgiValidatorAndProcess<Resource> vp : processList())
        {
            if (vp.validator == null)
            {
                logger.trace("doProcess#process : {}", vp.process);
                vp.process.doProcess(activity);
                continue;
            }

            logger.trace("doProcess#validator : {}", vp.validator);
            if (vp.validator.doValidate(activity))
            {
                continue;
            }

            logger.trace("doProcess#errorProcess : {}", vp.process);
            vp.process.doProcess(activity);
            return;
        }
    }

    /**
     * @param accessor
     * @param parameters
     */
    private void setParameters(final PojoAccessor<Resource> accessor,
            final HashMap<String, Object> parameters)
    {
        final List<String> keys = this.requestParameterKeys();
        for (final String key : keys)
        {
            final Object value = parameters.get(key);
            accessor.set(key, value);
        }
    }

    /**
     * constractor injection
     * 
     * @param accessorProvider
     */
    public WsgiControllerImple(
        final Provider<PojoAccessor<Resource>> accessorProvider)
    {
        this.accessorProvider = accessorProvider;
    }

    private final Provider<PojoAccessor<Resource>> accessorProvider;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
}